/**HEADER********************************************************************
* 
* Copyright (c) 2011 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: lwgpio_mcf51ag.c$
* $Version : 3.8.2.0$
* $Date    : Sep-22-2011$
*
* Comments:
*
*   This file contains the GPIO standard functions used on boards
*
*END************************************************************************/

#include "mqx.h"
#include "bsp.h"
#include "lwgpio.h"

/*** FLAGS for LWGPIO_STRUCT.flags ***/

/************************************************************************************************
 * pull_down * pull_up * int.mask * int.src * direction * int.mode * port number  *  pin index  *
 *   1 bit   *  1 bit  *  8bits   *  2bits  *   1 bit   *   3bits  *    8bits     *    3bits    *
 ***********************************************************************************************/

/* - pin index */
#define LWGPIO_PIN_SHIFT (0)
#define LWGPIO_PIN_MASK (0x7 << LWGPIO_PIN_SHIFT)
#define LWGPIO_PIN(x) (((x) << LWGPIO_PIN_SHIFT) & LWGPIO_PIN_MASK)
#define LWGPIO_PIN_FROM_ID(id) (((id) & LWGPIO_PIN_MASK) >> LWGPIO_PIN_SHIFT)

/* - port number */
#define LWGPIO_PORT_SHIFT (3)
#define LWGPIO_PORT_MASK (0xFF << LWGPIO_PORT_SHIFT)
#define LWGPIO_PORT(x) (((x) << LWGPIO_PORT_SHIFT) & LWGPIO_PORT_MASK)
#define LWGPIO_PORT_FROM_ID(id) (((id) & LWGPIO_PORT_MASK) >> LWGPIO_PORT_SHIFT)

/* - int. mode */
#define LWGPIO_INT_MODE_SHIFT (11)
#define LWGPIO_INT_MODE_MASK (0x03 << LWGPIO_INT_MODE_SHIFT)
#define LWGPIO_INT_MODE(x) (((x) << LWGPIO_INT_MODE_SHIFT) & LWGPIO_INT_MODE_MASK)
#define LWGPIO_INT_MODE_FROM_ID(id) (((id) & LWGPIO_INT_MODE_MASK) >> LWGPIO_INT_MODE_SHIFT)

/* - direction */ 
/*
#define LWGPIO_DIR_SHIFT (13)
#define LWGPIO_DIR_MASK (0x01 << LWGPIO_DIR_SHIFT)
*/

/* - int. source (KBI1, KBI2, IRQ pin) */
#define LWGPIO_INT_SRC_SHIFT (14)
#define LWGPIO_INT_SRC_MASK (0x03 << LWGPIO_INT_SRC_SHIFT)
#define LWGPIO_INT_SRC(x) (((x) << LWGPIO_INT_SRC_SHIFT) & LWGPIO_INT_SRC_MASK)
#define LWGPIO_INT_SRC_FROM_ID(id) (((id) & LWGPIO_INT_SRC_MASK) >> LWGPIO_INT_SRC_SHIFT) 

/* - int. mask */
#define LWGPIO_INT_PINMASK_SHIFT (16)
#define LWGPIO_INT_PINMASK_MASK (0xFF << LWGPIO_INT_PINMASK_SHIFT)
#define LWGPIO_INT_PINMASK(x) (((x) << LWGPIO_INT_PINMASK_SHIFT) & LWGPIO_INT_PINMASK_MASK)
#define LWGPIO_INT_PINMASK_FROM_ID(id) (((id) & LWGPIO_INT_PINMASK_MASK) >> LWGPIO_INT_PINMASK_SHIFT) 

/* - Pull-up/down */
#define LWGPIO_PULL_UP_SHIFT    (25)
#define LWGPIO_PULL_DOWN_SHIFT  (26)
#define LWGPIO_PULL_UP_MASK     (0x01 << LWGPIO_PULL_UP_SHIFT)
#define LWGPIO_PULL_DOWN_MASK   (0x01 << LWGPIO_PULL_DOWN_SHIFT)

/* interrupt sources */
#define LWGPIO_INT_SRC_KBIAE    (0)
#define LWGPIO_INT_SRC_KBIFJ    (0)

/* Define range for digital filter*/
#define LWGPIO_AVAL_DIGITAL_FILTER_MIN  (0x100)
#define LWGPIO_AVAL_DIGITAL_FILTER_MAX  (0x19F)

/*** GLOBALS ***/

static const VMCF51AG_GPIO_DATA_STRUCT_PTR lwgpio_data_ptr[GPIO_PORT_MAX] =
{
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_A_G[GPIO_PORT_A]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_A_G[GPIO_PORT_B]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_A_G[GPIO_PORT_C]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_A_G[GPIO_PORT_D]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_A_G[GPIO_PORT_E]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_A_G[GPIO_PORT_F]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_A_G[GPIO_PORT_G]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_H_J[0]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_H_J[1])
};

static const VMCF51AG_GPIO_STRUCT_PTR lwgpio_ptr[GPIO_PORT_MAX] =
{
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_A]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_B]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_C]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_D]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_E]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_F]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_G]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_H]),
    &(((VMCF51AG_STRUCT_PTR)BSP_IPSBAR)->GPIO_P[GPIO_PORT_J])   
};

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_init
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Decodes ID to HW specific struct and then performs pin initialization
*
*END*********************************************************************/
boolean lwgpio_init
(
    /* Pointer to LWGPIO internal structure to be filled in */
    LWGPIO_STRUCT_PTR handle,
    /* Pin ID, bitmask integer value */
    LWGPIO_PIN_ID     id,
    /* Direction to be set within initialization */
    LWGPIO_DIR        dir,
    /* Value to be set within initialization */
    LWGPIO_VALUE      value
)
{ /* Body */

    uint_32 port_idx, pin_idx;

    port_idx = LWGPIO_PORT_FROM_ID(id);
    pin_idx = LWGPIO_PIN_FROM_ID(id);

    handle->port_idx    = port_idx;
    handle->pinmask     = 1 << pin_idx;
    handle->flags       = id; 

    /* Set value prior to set to output */
    if (value != LWGPIO_VALUE_NOCHANGE) {
        /* Note: there is no check for values not defined as LWGPIO_VALUE enum */
        lwgpio_set_value(handle, value);
    }

    if (dir != LWGPIO_DIR_NOCHANGE) {
        /* Note: there is no check for values not defined as LWGPIO_DIR enum */
        lwgpio_set_direction(handle, dir);
    }

    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_attribute
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Sets attributes
*
*END*********************************************************************/
boolean lwgpio_set_attribute
(
    /* Pin handle to get function from */
    LWGPIO_STRUCT_PTR  handle,
    /* PORT attribute */
    uint_32 attribute_id,
    /* Attribute value */
    uint_32 value
)
{ 
    switch(attribute_id) 
    {
        case(LWGPIO_ATTR_PULL_UP):
        { 
            if(value == LWGPIO_AVAL_ENABLE)
            {
                /* PULL enable */
                lwgpio_ptr[handle->port_idx]->PTxPUE |= handle->pinmask;
                /* pullup resistor is selected */
                lwgpio_ptr[handle->port_idx]->PTxPUS |= handle->pinmask;
                /* setting pull up flag */
                handle->flags |= LWGPIO_PULL_UP_MASK ;
                handle->flags &= ~LWGPIO_PULL_DOWN_MASK ;
            
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                if(!(handle->flags & LWGPIO_PULL_DOWN_MASK ))
                {
                    lwgpio_ptr[handle->port_idx]->PTxPUE &= ~(handle->pinmask);
                    /* clearing pullup flag */
                    handle->flags &= ~LWGPIO_PULL_UP_MASK ;
                }
                /* if pull_down flag is set do nothing and return is true*/	
            }
            break;
        }
        case(LWGPIO_ATTR_PULL_DOWN):
        {
            if(value == LWGPIO_AVAL_ENABLE)
            {
                /* PULL enable */
                lwgpio_ptr[handle->port_idx]->PTxPUE |= handle->pinmask;
                /* pulldown resistor is selected */
                lwgpio_ptr[handle->port_idx]->PTxPUS &= ~(handle->pinmask);
                /* setting pulldown flag */
                handle->flags |= LWGPIO_PULL_DOWN_MASK ;
                handle->flags &= ~LWGPIO_PULL_UP_MASK ;
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                if(!(handle->flags & LWGPIO_PULL_UP_MASK ))
                {
                    lwgpio_ptr[handle->port_idx]->PTxPUE &= ~(handle->pinmask);
                    /* clearing pulldown flag */
                    handle->flags &= ~LWGPIO_PULL_DOWN_MASK ;
                }
                /* if pull_up flag is set do nothing and return is true */
            }
            break;
        }
        case(LWGPIO_ATTR_SLEW_RATE):
        {
            if(value == LWGPIO_AVAL_ENABLE)
            {
                lwgpio_ptr[handle->port_idx]->PTxSRE |= handle->pinmask;
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                lwgpio_ptr[handle->port_idx]->PTxSRE &= ~(handle->pinmask);
            }
            break;
        }
        case(LWGPIO_ATTR_PASIVE_FILTER):
        {
            if(value == LWGPIO_AVAL_ENABLE)
            {
                lwgpio_ptr[handle->port_idx]->PTxPFE |= handle->pinmask;
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                lwgpio_ptr[handle->port_idx]->PTxPFE &= ~(handle->pinmask);
            }
            break;
        }
        case(LWGPIO_ATTR_DRIVE_STRENGTH):
        {
            if(value == LWGPIO_AVAL_DRIVE_STRENGTH_HIGH)
            {
                lwgpio_ptr[ handle->port_idx ]->PTxDS |= handle->pinmask;
            }
            else    /* value == LWGPIO_AVAL_DRIVE_STRENGTH_LOW */
            {
                lwgpio_ptr[ handle->port_idx ]->PTxDS &= ~(handle->pinmask);
            }
            break;
        }
        case(LWGPIO_ATTR_DIGITAL_FILTER):
        {
            switch(value)
            {
                case(LWGPIO_AVAL_ENABLE):
                {
                    /* default settings: bus clock and 1 clock cycle*/
                    lwgpio_ptr[handle->port_idx]->PTxDFE |= handle->pinmask; 
                    break;
                }
                case(LWGPIO_AVAL_DISABLE):
                {
                    lwgpio_ptr[handle->port_idx]->PTxDFE &= ~(handle->pinmask); 
                    break;
                }
                default:
                {
                    /* Checking range */
                    if((value >= LWGPIO_AVAL_DIGITAL_FILTER_MIN ) && (value <= LWGPIO_AVAL_DIGITAL_FILTER_MAX))
                    {
                        lwgpio_ptr[handle->port_idx]->PTxDFE |= handle->pinmask; 
                        lwgpio_ptr[handle->port_idx]->PTxDFC = (value & 0xFF);
                    }
                    else /* Out of range */
                    {
                        return FALSE;
                    }
                }
            }
            break;
        }
        default:
        {
           return FALSE;
        }
    }
   return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_functionality
* Returned Value   : void
* Comments         :
*    Sets functionality (peripheral mode) of the pin
*
*END*********************************************************************/
void lwgpio_set_functionality
(
    /* Pin handle to set function on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Function to be set (integer value) */
    uint_32            function
)
{
    return;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_functionality
* Returned Value   : void
* Comments         :
*    Gets function (peripheral mode) of the pin
*
*END*********************************************************************/
uint_32 lwgpio_get_functionality
(
    /* Pin handle to get function from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return 0;
}


/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_direction
* Returned Value   : void
* Comments         :
*    Sets direction of the pin
*
*END*********************************************************************/
void lwgpio_set_direction
(
    /* Pin handle to set direction on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Direction to be set */
    LWGPIO_DIR         dir
)
{
    if ( dir == LWGPIO_DIR_INPUT )
    {
        lwgpio_data_ptr[ handle->port_idx ]->PTxDD &= ~handle->pinmask;
    }
    else if ( dir == LWGPIO_DIR_OUTPUT )
        lwgpio_data_ptr[ handle->port_idx ]->PTxDD |= handle->pinmask;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_value
* Returned Value   : void
* Comments         :
*    Sets value (output latch) of the pin
*
*END*********************************************************************/
void lwgpio_set_value
(
    /* Pin handle to set value on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Direction to be set */
    LWGPIO_VALUE       out_value
)
{
    if ( out_value == LWGPIO_VALUE_LOW )
        lwgpio_data_ptr[ handle->port_idx ]->PTxD &= ~handle->pinmask;
    else if ( out_value == LWGPIO_VALUE_HIGH )
        lwgpio_data_ptr[ handle->port_idx ]->PTxD |= handle->pinmask;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_toggle_value
* Returned Value   : void
* Comments         :
*    Toggles value of output latch of the pin
*
*END*********************************************************************/
void lwgpio_toggle_value
(
    /* Pin handle to toggle value on */
    LWGPIO_STRUCT_PTR  handle
)
{
    lwgpio_set_value(handle, lwgpio_get_value(handle) == LWGPIO_VALUE_HIGH ? LWGPIO_VALUE_LOW : LWGPIO_VALUE_HIGH);    
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_value
* Returned Value   : LWGPIO_VALUE of pin status
* Comments         :
*    Returns value (output latch or read data) of the pin
*
*END*********************************************************************/
LWGPIO_VALUE lwgpio_get_value
(
    /* Pin handle to get value from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return ( lwgpio_data_ptr[ handle->port_idx ]->PTxD & handle->pinmask ) ? LWGPIO_VALUE_HIGH : LWGPIO_VALUE_LOW;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_raw
* Returned Value   : LWGPIO_VALUE of pin status
* Comments         :
*    Returns read level of the pin
*
*END*********************************************************************/
LWGPIO_VALUE lwgpio_get_raw
(
    /* Pin handle to get value from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return ( lwgpio_data_ptr[ handle->port_idx ]->PTxD & handle->pinmask ) ? LWGPIO_VALUE_HIGH : LWGPIO_VALUE_LOW;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_init
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Initializes pin to generate interrupt
*
*END*********************************************************************/
boolean lwgpio_int_init
(
    /* Pin handle to initialize interrupt on */
    LWGPIO_STRUCT_PTR handle,
    /* Interrupt mode */
    LWGPIO_INT_MODE   mode
)
{  
    uchar int_enabled; 

    switch( handle->port_idx ) {
    
        case GPIO_PORT_C:
        case GPIO_PORT_G:
        case GPIO_PORT_H:
        /* this three has only 7 pins */
            if ( handle->pinmask & 0x80 )
                return FALSE;
            break;

        default:
            break;            
    }    
      
    /* for the next code:
       MCF51XX_KBIXSC_KBIE, MCF51XX_KBIXSC_KBMOD and MCF51XX_KBIXSC_KBIE are
       KBIxSC pinmasks valid also for appropriate IRQSC pins */
    
    /* mask ints */
    int_enabled = lwgpio_ptr[ handle->port_idx ]->PTxIC & MCF51XX_KBIXSC_KBIE; 
    lwgpio_ptr[ handle->port_idx ]->PTxIC &= ~MCF51XX_KBIXSC_KBIE; 
        
    /* select pin polarity */
    if ( mode == LWGPIO_INT_MODE_FALLING || mode == LWGPIO_INT_MODE_LOW ) {    
        lwgpio_ptr[ handle->port_idx ]->PTxIES &= ~handle->pinmask;
    }
    else if ( mode == LWGPIO_INT_MODE_RISING || mode == LWGPIO_INT_MODE_HIGH ) {
        lwgpio_ptr[ handle->port_idx ]->PTxIES |= handle->pinmask;
    }
    else {
        if( int_enabled )        
            lwgpio_ptr[ handle->port_idx ]->PTxIC |= MCF51XX_KBIXSC_KBIE;
        return FALSE;
    }

    /* want level detect? */              
    if( mode == LWGPIO_INT_MODE_LOW || mode == LWGPIO_INT_MODE_HIGH )
        lwgpio_ptr[ handle->port_idx ]->PTxIC |= MCF51XX_KBIXSC_KBIMOD;
        
    /* pull up / pull down */    
    /* -> let the user do this */
    
    /* enable ints */
    lwgpio_ptr[ handle->port_idx ]->PTxIPE |= handle->pinmask;
    
    asm("NOP");
    asm("NOP");
    asm("NOP");
    
    /* clear false ints */
    lwgpio_ptr[ handle->port_idx ]->PTxIF |= 0xff;
        
    if( int_enabled )        
        lwgpio_ptr[ handle->port_idx ]->PTxIC |= MCF51XX_KBIXSC_KBIE;
        
    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_enable
* Returned Value   : void
* Comments         :
*    Enables / disables interrupts for specified pin
*
*END*********************************************************************/
void lwgpio_int_enable
(
    /* Pin handle to enable interrupt on */
    LWGPIO_STRUCT_PTR handle,
    /* Enable or disable interrupt? TRUE = enable */
    boolean           ena
)
{
    /* MCF51XX_KBIXSC_KBIE is valid IE pin mask also for IRQSC */
    if( ena )        
        lwgpio_ptr[ handle->port_idx ]->PTxIC |= MCF51XX_KBIXSC_KBIE;
    else
        lwgpio_ptr[ handle->port_idx ]->PTxIC &= ~MCF51XX_KBIXSC_KBIE;
    return; 
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_get_flag
* Returned Value   : TRUE if interrupt flag is set
* Comments         :
*    Checks if there is pending interrupt flag for specified pin
*
*END*********************************************************************/
boolean lwgpio_int_get_flag
(
    /* Pin handle to get interrupt flag on */
    LWGPIO_STRUCT_PTR handle
)
{
        /* MCF51XX_KBIXSC_KBF is valid interrupt flag pin mask also for IRQSC */ 
    return lwgpio_ptr[ handle->port_idx ]->PTxIF & handle->pinmask ? TRUE : FALSE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_clear_flag
* Returned Value   : void
* Comments         :
*    Clears pending interrupt flag on peripheral
*
*END*********************************************************************/
void lwgpio_int_clear_flag
(
    /* Pin handle to clear interrupt flag on */
    LWGPIO_STRUCT_PTR handle
)
{
    /* MCF51XX_KBIXSC_KBACK is valid ACK pin mask also for IRQSC */
    lwgpio_ptr[ handle->port_idx ]->PTxIF = handle->pinmask;       
    return;   
}


/*FUNCTION****************************************************************
* 
* Function Name    : lwgpio_int_get_vector
* Returned Value   : vector index
* Comments         :
*    This function returns vector index for specified pin
*
*END*********************************************************************/
uint_32 lwgpio_int_get_vector
(
    /* Pin handle to get vector of */
    LWGPIO_STRUCT_PTR  handle
)
{
     if( handle->port_idx < GPIO_PORT_F )
         return MCF51AG_INT_Vkbiae;
     else 
         return MCF51AG_INT_Vkbifj;
}

/* EOF */
